#include "vm/spage.h"
#include "filesys/file.h"
#include "filesys/filesys.h"
#include "filesys/off_t.h"
#include "../lib/kernel/hash.h"
#include "threads/thread.h"
#include "threads/palloc.h"
#include "userprog/pagedir.h"
#include "threads/vaddr.h"
#include "threads/malloc.h"
#include "vm/frame.h"
#include <stdio.h>

extern struct lock file_lock;

void
get_spage(struct file *file, off_t ofs, uint8_t *upage, uint32_t read_bytes, uint32_t zero_bytes, bool writable)
{
  struct thread *t = thread_current();
  struct spage *p = malloc (sizeof (struct spage));
  struct hash_elem *e;
//  struct list_elem *e;

  p->vaddr = upage;
  p->file = file;
  p->file_ofs = ofs;
  p->read_bytes = read_bytes;
  p->zero_bytes = zero_bytes;
  p->writable = writable;

  p->valid = false;

  e = hash_insert (&t->spage_table, &p->elem);
//  list_push_back (&t->spage_table, &p->elem);
  return;
}

void
free_spage( void *vaddr){
  struct thread * cur = thread_current();
  struct spage * target = spage_lookup(vaddr);
  struct hash_elem *del;
  if (target == NULL)
    return; // not found
  else{
    /* write to file if page is dirty */
    if(pagedir_is_dirty(cur->pagedir,target->vaddr)){
      lock_acquire(&file_lock);
      file_seek(target->file,target->file_ofs);
      file_write(target->file,target->vaddr,PGSIZE);
      lock_release(&file_lock);
    }

    /* free frame table bound to this page*/
    void * paddr = pagedir_get_page(cur->pagedir,vaddr);//get physical address
    /* if paddr == NULL, the vaddr was never accessed so it may not be installed in pagedir */
    if(paddr != NULL) 
      free_frame(paddr);

    del = hash_delete(&cur->spage_table,&target->elem); //delete from the hash table
    free(target); //free allocated memory of spage struct
  }
}


unsigned spage_hash_func (struct hash_elem *p_, void *aux UNUSED)
{
  const struct spage *p = hash_entry (p_, struct spage, elem);
  return hash_bytes (&p->vaddr, sizeof p->vaddr);
//  return 0;
}
 

bool spage_less_func (struct hash_elem *a_, struct hash_elem *b_, void *aux UNUSED)
{
  const struct spage *a = hash_entry (a_, struct spage, elem);
  const struct spage *b = hash_entry (b_, struct spage, elem);

  return a->vaddr < b->vaddr;
//  return 0;
}

struct spage *
spage_lookup (void *vaddr)
{

//  ASSERT(&thread_current()->spage_table != NULL);
//  ASSERT(vaddr != NULL);

  struct spage p;
  struct hash_elem *e;
//  struct list_elem *e;

  p.vaddr = vaddr;
  
  e = hash_find (&thread_current()->spage_table, &p.elem);
//  e = NULL;

//  for (e = list_begin(&thread_current() -> spage_table); e != list_end(&thread_current()->spage_table); e = list_next(e))
//  {
//    p = list_entry(e, struct spage, elem);
//    if (p->vaddr == vaddr)
//      return p;  
//  }

//  return NULL;

//  if (e)
//    return NULL;
//  else
//    return hash_entry(e, struct spage, elem);

  return e != NULL ? hash_entry (e, struct spage, elem) : NULL;

}
/*

void
mapping(struct file *file, off_t ofs, uint8_t *upage, uint32_t read_bytes, uint32_t zero_bytes, bool writable)
{
  ASSERT ((read_bytes + zero_bytes) % PGSIZE == 0);
  ASSERT (pg_ofs (upage) == 0);
  ASSERT (ofs % PGSIZE == 0);
 
  lock_acquire(&file_lock);
  filesys_open(file);
  lock_release(&file_lock);

  lock_acquire(&file_lock);
  file_seek(file, ofs);
  lock_release(&file_lock);

  uint8_t *kpage = get_frame();

  lock_acquire(&file_lock);
  file_read (file, kpage, read_bytes);
  lock_release(&file_lock);

  memset (kpage + read_bytes, 0, zero_bytes);

  install_page_ (upage, kpage, writable);

  return;
}


bool
install_page_ (uint8_t *upage, uint8_t *kpage, bool writable)
{
  struct thread *t = thread_current ();

  return (pagedir_get_page (t->pagedir, (void*)upage) == NULL
	  && pagedir_set_page (t->pagedir, (void*)upage, (void*)kpage, writable));
}
 */
